/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-05
 * V4.0
 */
package com.jphenix.servlet.filter;

import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.jphenix.driver.log.FLog;
import com.jphenix.driver.log.xlogc.XLogFilter;
import com.jphenix.driver.nodehandler.FNodeHandler;
import com.jphenix.kernel.objectloader.FBeanFactory;
import com.jphenix.kernel.objectloader.interfaceclass.IBean;
import com.jphenix.kernel.objectloader.interfaceclass.IBeanFactory;
import com.jphenix.kernel.objectloader.util.BeanSearcher;
import com.jphenix.servlet.common.HttpServletRequestImpl;
import com.jphenix.servlet.common.HttpServletResponseImpl;
import com.jphenix.share.lang.SBoolean;
import com.jphenix.share.lang.SLong;
import com.jphenix.share.lang.SString;
import com.jphenix.share.lang.SortVector;
import com.jphenix.share.tools.FileCopyTools;
import com.jphenix.share.tools.PidUtil;
import com.jphenix.share.util.BaseUtil;
import com.jphenix.share.util.SFilesUtil;
import com.jphenix.share.util.ServiceInfo;
import com.jphenix.standard.docs.BeanInfo;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.log.ILog;
import com.jphenix.standard.servlet.IBytesFilter;
import com.jphenix.standard.servlet.IFilter;
import com.jphenix.standard.servlet.IRequest;
import com.jphenix.standard.servlet.IResponse;
import com.jphenix.standard.servlet.IServletConst;
import com.jphenix.standard.viewhandler.INodeHandler;
import com.jphenix.standard.viewhandler.IViewHandler;
import com.jphenix.webserver.service.ServStarter;

/**
 * 过滤器总管
 * 注意:该类声明信息并不需要在配置文件中进行配置
 *       类主键固定为IFilterExplorer.BEAN_ID值
 * 
 * 
 * 过滤器中是无法知道内部虚拟路径的
 * 因为模块注册由Servlet总管负责，比如：解析出虚拟路径为 aa 该虚拟路径可能没有
 * 对应模块，而是根模块的一个子路径
 * 
 *   *****************************************************************************************
 *   整个架构默认只处理脚本代码。默认抛弃执行 WEB-INF/classes 中的传统类。除非在配置文件中设置：
 *   <enabled_search_path_classes>true</enabled_search_path_classes>
 *   *****************************************************************************************
 * 
 * 动作扩展名，如果适应全部动作路径，则标记* ，如果没有动作扩展名，则标记-
 * 
 * 在配置文件中增加 <enabled_filter_no_cache>true</enabled_filter_no_cache>可以屏蔽页面缓存
 * 
 * 注意：关于getServletPath()和getPathInfo()，我们发现Tomcat中getPathInfo()永远返回空，我们也针对这种情况不再对getPathInfo做处理
 * 
 * 
 * 2018-08-10 改为设置一个变量，通过变量值判断是否禁用缓存
 * 2018-09-05 从配置文件中获取是否禁用页面缓存功能
 *            修改了构造工厂类时的初始化顺序
 * 2018-09-13 简化了整个架构中日志初始化步骤
 * 2019-01-24 修改了新的资源管理类路径
 * 2019-04-16 修改了初始化时获取config对象为空的错误。去掉了强制编码格式转换，因为所有请求都经过这个类，包括大量的图片，样式文件等等
 * 2019-05-16 halt后，有些连接的Tcp Socket 会出现TIME_WAIT 和 CLOSE_WAIT，所以遇到这种情况时，在配置文件中增加<system_no_halt>true</system_no_halt>
 * 2019-08-14 对图片做了缓存处理
 * 2019-08-31 增加了点注释：关于getServletPath()和getPathInfo()
 * 2020-03-27 在执行终止系统之前，先输出一条日志。有时候执行终止时，卡在那里
 * 2020-03-28 找到系统莫名终止并且无法自动重启。是因为通过终端软件到linux控制台，在控制台执行启动命令时，末尾没有加&符号，导致终止控制台时，程序收到停止信号导致的。
 * 2020-06-30 构造HttpServletRequestImpl类实例时，将当前类实例传入其中
 * 2020-08-02 去掉了内置处理内部文件，改用专门的过滤器来处理
 * 2020-08-20 强制使用 web.xml中配置的请求编码格式（Tomcat9提交中文乱码，以前版本没问题的，妈的）
 * 2020-08-26 调用动作拦截异常从Exception改为Throwable
 * 2021-03-17 整理了代码缩进
 * 
 * com.jphenix.servlet.filter.FilterExplorer
 * @author 刘虻
 * 2010-5-25 下午01:33:14
 */
@ClassInfo({"2021-03-17 19:16","过滤器总管"})
@BeanInfo({"filterexplorer"})
public class FilterExplorer implements Filter {

  protected IBeanFactory bf                 = null;  //类加载器
  protected FilterConfig config             = null;  //初始化配置文件
  protected FilterVector fv                 = null;  //过滤器类容器
  protected long totalCount                 = 0;     //当前请求数
  protected String webBasePath              = null;  //网站根路径
  protected String webInfBasePath           = null;  //网站WEB-INF根路径 
  protected String actionExtName            = "ha";  //动作扩展名
  protected String contextPath              = null;  //虚拟路径
  protected String requestEncoding          = null;  //请求默认编码格式

  protected boolean disabledInsideMapping   = false; //是否禁止内部资源输出
  protected Map<String,String> errorPageMap = null;  //错误页面对照容器
  protected String filterUrlPatternPath     = null;  //过滤器响应路径（不包含末尾的文件过滤名或通配符）
  public    boolean enabledNoCache          = false; //是否允许禁用缓存（不包含图片）
  public    boolean enabledImgNoCache       = false; //是否允许禁用图片缓存

  protected List<String> welcomeFileList       = new ArrayList<String>();       //默认显示的文件
  protected List<IBytesFilter> bytesFilterList = new ArrayList<IBytesFilter>(); //字节过滤序列

  /**
   * 构造函数
   * @author 刘虻
   */
  public FilterExplorer() {
    super();
  }

  /**
   * 获取当前类加载器
   * @return 当前类加载器
   * 2017年11月24日
   * @author MBG
   */
  public IBeanFactory getBeanFactory() {
    return bf;
  }

  /**
   * 覆盖方法
   * 刘虻
   * 2010-5-25 下午01:33:14
   */
  @Override
    public void destroy() {
    //构建错误信息
    StringBuffer errorMsg = new StringBuffer();
    errorMsg.append("\n\n***************************************************\n      Warning: The Filter Has Be Destroy!!!!\n***************************************************\n\n");

    //输出当前堆栈
    Throwable ex = new Throwable();
    StackTraceElement[] stackElements = ex.getStackTrace();
    for(StackTraceElement ele:stackElements) {
      errorMsg
        .append("[")
        .append(ele.getClassName())
        .append("] [")
        .append(ele.getFileName())
        .append("] [")
        .append(ele.getLineNumber())
        .append("] [")
        .append(ele.getMethodName())
        .append("]\n");
    }
    errorMsg.append("\n\n");
      
    System.err.println(errorMsg);
    if(bf!=null) {
      try {
        FLog.getLog(bf.getLogShadow(),this).warning(errorMsg,null);
      }catch(Exception e) {}
    }

    /* 有时候Tomcat或者其他Servlet容器会执行这个该死的方法，导致
      * 应用半死不活成为僵尸应用，不如在这里快刀斩乱麻直接终止java
      * 进程，同行在配置启动应用脚本时，会配置成死循环，终止java进程
      * 后会自动再次运行改进程
      * 注意，不能使用 System.exit(1); 因为如果程序
      * 在别的地方用了Runtime.getRuntime().addShutdownHook(Thread); 
      * 而传入的线程发生了阻塞，就不会停止进程了
      * 注意：halt后，有些连接的Tcp Socket 会出现TIME_WAIT 和 CLOSE_WAIT
      * ，所以遇到这种情况时，在配置文件中增加<system_no_halt>true</system_no_halt>
      * 
      * 注意：遇到一种情况，会导致执行该方法，最终会卡住无法重启。
      * 
      *       在linux控制台启动程序时，并没有在末尾加 & 标记为后台执行，导致关闭SSH或
      *       其它终端软件后，控制台在结束前，给java发送了结束信号，导致程序进入到这个
      *       方法中，并且无法重启。
      * 
      * 
      */
    if(bf!=null && SBoolean.valueOf(bf.getConfProp().getParameter("system_no_halt"))) {
      System.err.print("******System Begin Exit******");
      System.exit(1);
    }else {
      System.err.print("******System Begin Halt******");
      Runtime.getRuntime().halt(1);
    }
    //注意在此销毁类工厂后，就再也启动不了了
    //RootBeanFactory.destroyBeanFactory();
  }

  /**
   * 覆盖方法
   * 刘虻
   * 2010-5-25 下午01:33:14
   */
  @Override
    public void doFilter(
      ServletRequest  request,
      ServletResponse response,
      FilterChain     filterChain) throws IOException, ServletException {
    totalCount++;
    IRequest  req;  //页面请求
    IResponse resp; //页面反馈
    if(request instanceof IRequest) {
      req = (IRequest)request;
    }else {
      //构造安全页面请求处理类
      req = new HttpServletRequestImpl(new SecureRequestWrapper((HttpServletRequest)request,config),this,contextPath);
      //设置网站根路径
      ((HttpServletRequestImpl)req).setWebBasePath(webBasePath);
    }
    //强制使用编码格式
    req.setCharacterEncoding(requestEncoding);
    //页面反馈
    if(response instanceof IResponse) {
      resp = (IResponse)response;
    }else {
      resp = new HttpServletResponseImpl((HttpServletResponse)response,this,errorPageMap);
    }
    String extName; //动作扩展名
    //动作路径
    String action = req.getServletPath();
    int point     = action.lastIndexOf("."); //动作扩展名分割点
    if(point<0) {
      extName = "-";
    }else {
      extName = action.substring(point+1).toLowerCase();
    }
    try {
      if("info".equals(extName)) {
        totalCount--;
        outInfo(req,resp);
        return;
      }
      /*
       * 有时候需要兼顾这种请求 /000001.ha/query
       * 比如 SimpleJson请求
       * 或者类似PHP风格的请求
       */
      point = extName.indexOf("/");
      if(point>0) {
        //将后面附加的路径作为参数_method
        req.setParameter("_method",extName.substring(point+1));
        extName = extName.substring(0,point);
      }
      List<String> fileExtNameList; //响应扩展名序列
      IFilter ele; //过滤器元素
      for(int i=0;i<fv.getFilterCount();i++) {
        fileExtNameList = fv.getFilterExtNameList(i);
        //获取过滤器元素
        if(fileExtNameList.contains(extName) || fileExtNameList.contains("*")) {
          ele = fv.getFilter(i);
          if(ele.doFilter(req,resp)) {
            totalCount--;
            return;
          }else if(ele instanceof SpecialUrlFilter) {
            //动作路径
            action = req.getServletPath();
            point  = action.lastIndexOf("."); //动作扩展名分割点
            if(point<0) {
              extName = "-";
            }else {
              extName = action.substring(point+1).toLowerCase();
            }
          }
        }
      }
      if(SString.valueOf(req.getHeader("Accept")).startsWith("image/")) {
        if(enabledImgNoCache) {
          resp.setHeader("Cache-Control","no-store");
        }else {
          resp.setHeader("Cache-Control", " max-age=31536000, must-revalidate");
        }
      }else if(enabledNoCache && !extName.startsWith("woff")) {
        //如果设置了这个变量为真，则禁用缓存
        resp.setHeader("Cache-Control","no-store");
      }else {
        resp.setHeader("Cache-Control", " max-age=31536000, must-revalidate");
      }
      if(filterChain!=null) {
        if("-".equals(extName)) {
          if(!servWelcome(req,resp)) {
            filterChain.doFilter(req,resp);
          }
        }else {
          filterChain.doFilter(req,resp);
        }
      }
    }catch(Throwable e) {
      if(e.getClass().getName().indexOf("ClientAbortException")>-1) {
        //客户端取消接收数据
        totalCount--;
        return;
      }
      e.printStackTrace();
      (resp).sendError(500);
    }
    totalCount--;
  }
  
  /**
   * 输出指定文件内容到页面中
   * 刘虻
   * 2012-7-27 上午9:09:36
   * @param path         文件路径
   * @param req          页面请求
   * @param res          页面反馈
   * @return             true处理完毕  false未做任何处理
   * @throws IOException 异常
   */
  public boolean serveFile (
      String    path,
      IRequest  req,
      IResponse res) throws Exception {
    //构建目标文件对象
    File file = new File(SFilesUtil.getAllFilePath(path,webBasePath));
    if(!file.exists()) {
      return false;
    }
    if(req!=null) {
      //获取字节信息过滤器
      ByteFilter byteFilter = bf.getObject(ByteFilter.class,this);
      if(byteFilter!=null) {
        byteFilter.serveFile(req,res,file);
      }
      return true;
    }
    if(!file.canRead()) {
      res.sendError(HttpServletResponse.SC_FORBIDDEN);
      return true;
    }else {
      // by Niel Markwick
      try {
        file.getCanonicalPath();
      } catch (Exception e) {
        res.sendError(HttpServletResponse.SC_FORBIDDEN,"Forbidden, exception:" + e);
        return true;
      }
    }
    // Handle If-Modified-Since.
    res.setStatus(HttpServletResponse.SC_OK);
    // 设置内容类型
    res.setContentType(getFilterConfig().getServletContext().getMimeType(file.getName()));
    res.setHeader("Cache-Control", "no-cache");
    res.setHeader("Pragma", "no-cache");
    res.setDateHeader("Expires", 0); 
    //获取处理后的文件内容
    byte[] contentBytes;
    try {
      contentBytes= FileCopyTools.copyToByteArray(file);
    }catch(Exception e) {
      e.printStackTrace();
      res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
      return true;
    }
    if (contentBytes.length < Integer.MAX_VALUE) {
      res.setContentLength(contentBytes.length);
    }else {
      res.setHeader("Content-Length", Long.toString(contentBytes.length));
    }
    res.setDateHeader("Last-modified", file.lastModified());
    try {
      //输出内容
      res.getOutputStream().write(contentBytes);
    }catch(Exception e) {}
    return true;
  }

  /**
   * 重定位到欢迎页面
   * @param req         页面请求
   * @param resp        页面反馈
   * @return            返回真，跳转完毕
   * @throws Exception  异常
   * 2016年11月11日
   * @author MBG
   */
  protected boolean servWelcome(HttpServletRequest req, HttpServletResponse resp) throws Exception {
    File welcomeFile;
    String path = req.getServletPath(); //动作路径
    String reDir = null; //跳转url
    for(String file:welcomeFileList) {
      if(file.endsWith(actionExtName)) {
        reDir = file;
        break;
      }
      welcomeFile = new File(SFilesUtil.getAllFilePath(path+file,webBasePath));
      if(welcomeFile.exists()) {
        reDir = path+file;
        break;
      }
    }
    if(reDir==null) { 
      return false;
    }
    //上下文
    String contextPath = SString.valueOf(req.getContextPath());
    if(!"/".equals(contextPath) && contextPath.length()>0) {
      reDir = contextPath+reDir;
    }
    //执行页面跳转
    resp.sendRedirect(reDir);
    return true;
  }

  /**
   * 显示环境信息
   * @param req 页面请求
   * @param resp 页面返回
   * 2015年8月6日
   * @author 马宝刚
   */
  protected void outInfo(HttpServletRequest req, HttpServletResponse resp) {
    String action = req.getServletPath(); //动作路径
    ServletOutputStream os = null;
    try {
      os = resp.getOutputStream();
      if("/_show_.info".equals(action)) {
        //获取内存信息
        MemoryUsage memoryUsage = 
                ManagementFactory.getMemoryMXBean().getHeapMemoryUsage(); //椎内存使用情况
        os.println("<p>ActionCount:<b>"+totalCount+"</b></p>");
        os.println("<p>MaxMemory:<b>"+ SLong.showStringValueOf(memoryUsage.getMax(),3)+"</b></p>"); //最大可用内存
        os.println("<p>TotalMemory:<b>"+ SLong.showStringValueOf(memoryUsage.getInit(),3)+"</b></p>"); //初始的总内存
        os.println("<p>UsedMemory:<b>"+ SLong.showStringValueOf(memoryUsage.getUsed(),3)+"</b></p>"); //已使用的内存
      }else {
        os.println("No Support");
      }
    }catch(Exception e) {
    }finally {
      try {
        os.flush();
      }catch(Exception e) {}
      try {
        os.close();
      }catch(Exception e) {}
    }
  }

  /**
   * 获取字节过滤器
   * @return 字节过滤器
   * 2017年2月22日
   * @author MBG
   */
  public List<IBytesFilter> getByteFilterList() {
    return bytesFilterList;
  }

  /**
   * 获取过滤器配置信息类
   * 刘虻
   * 2013-4-7 上午11:25:05
   * @return 过滤器配置信息类
   */
  public FilterConfig getFilterConfig() {
    return config;
  }

  /**
   * 覆盖方法
   * 刘虻
   * 2010-5-25 下午01:33:14
   */
  @Override
  public void init(FilterConfig config) throws ServletException {
    this.config = config;
    //返回的路径中可能带有..需要整理一下
    webBasePath = 
      SFilesUtil.getAllFilePath(
        String.valueOf(config.getServletContext().getRealPath("/")),"");
    webInfBasePath = webBasePath;
    if(webInfBasePath.endsWith("/")) {
      webInfBasePath = webInfBasePath+"WEB-INF";
    }else {
      webInfBasePath = webInfBasePath+"/WEB-INF";
    }
    //默认请求编码格式
    requestEncoding = config.getInitParameter("encoding");
    if(requestEncoding==null || requestEncoding.length()<1) {
      requestEncoding = "UTF-8"; //默认UTF-8格式的
    }
    //获取配置文件路径
    String configFilePath = 
      config.getInitParameter(IServletConst.SERVLET_CONFIG_KEY_CONFIG_PATH);
    if(configFilePath==null || configFilePath.length()<1) {
      configFilePath = "resfiles/base.xml";
    }
    if(!SFilesUtil.isFileExist(configFilePath,webInfBasePath)) {
      configFilePath = ""; //没有加载外部配置文件，仅用内部配置文件
    }
    //需要类加载器额外加载的类文件全路径
    List<String> classPathList = new ArrayList<String>();
    //通过过滤器配置信息找到servlet-api.jar包的路径
    Class<?> confCls = getBaseFilterConfig(config.getClass());
    if(confCls!=null) {
      classPathList.add(confCls.getProtectionDomain().getCodeSource().getLocation().getPath());
    }
    ByteFilter byteFilter; //字节处理过滤器
    bf = FBeanFactory.getBeanFactory();
    //获取日志类
    ILog log = null;
    try {
      if(bf==null) {
        //获取根类加载器
        bf = FBeanFactory.getNewBeanFactory(webInfBasePath,false,true); 
        bf.setObject(FilterExplorer.class,this); //先将当前类设置到工厂中
        //获取过滤器类容器
        fv = new FilterVector(this,config);
        bf.setObject(FilterVector.class,fv);
        //加载配置文件，初始化整个网站
        FBeanFactory.initBeanFactory(bf,configFilePath,webInfBasePath,classPathList); 
        log = FLog.getLog(bf.getLogShadow(),this);
        //判断是否允许搜索WEB-INF/classes中的传统类
        if(SBoolean.valueOf(bf.getConfProp().getParameter("enabled_search_path_classes"))) {
          BeanSearcher.search(getWebInfPath()+"/classes",bf,log);
        }
        //构造字节信息过滤器
        byteFilter = new ByteFilter(bf);
        bf.setObject(ByteFilter.class,byteFilter);
        fv.regist(byteFilter);
        regist(byteFilter);
        byteFilter.setBeanFactory(bf);
      }else {
        fv = bf.getObject(FilterVector.class,this);
        byteFilter = bf.getObject(ByteFilter.class,this);
        log = FLog.getLog(bf.getLogShadow(),this);
      }
    }catch(Exception e) {
      e.printStackTrace();
    }
    //初始化服务器信息
    initServiceInfo(config,webBasePath);
    //初始化错误页面对照容器
    errorPageMap = new HashMap<String,String>();
    PidUtil.writePidFile(webBasePath+"/WEB-INF/lib"); //将当前的进程号写入文件
    filterUrlPatternPath = ""; //过滤器响应过滤路径
    //构建web.xml配置文件
    File webFile = new File(webBasePath+"/WEB-INF/web.xml");
    if(webFile.exists()) {
      //配置文件对象
      INodeHandler nh = FNodeHandler.newFile(webFile);
      //默认文件配置信息
      IViewHandler welcomeVh = 
        nh.getFirstChildNodeByNodeName("web-app")
          .getFirstChildNodeByNodeName("welcome-file-list");
      if(!welcomeVh.isEmpty()) {
        //获取文件名序列
        List<IViewHandler> welcomeVhList = 
          welcomeVh.getChildNodesByNodeName("welcome-file");
        String fileName;
        for(IViewHandler ele:welcomeVhList) {
          fileName = ele.nt().trim();
          if(fileName.length()<1) {
            continue;
          }
          welcomeFileList.add(fileName);
        }
      }
      //获取过滤器映射信息
      List<IViewHandler> mappingList = nh.cnn("filter-mapping");
      String filterName = SString.valueOf(config.getFilterName()); //过滤器名
      for(IViewHandler ele:mappingList) {
        if(filterName.equals(ele.fnn("filter-name").nt())) {
          filterUrlPatternPath = ele.fnn("url-pattern").nt();
        }
      }
      if(filterUrlPatternPath.length()>0) {
        //存在过滤信息
        int point = filterUrlPatternPath.lastIndexOf("/");
        if(point>0) {
          filterUrlPatternPath = filterUrlPatternPath.substring(0,point);
        }else {
          filterUrlPatternPath = "";
        }
      }
      //以下信息，如果web服务中配置了多个同类型的过滤器，
      //每个过滤器可以独立配置一些信息。这些独立信息放在
      //节点名就是过滤器名的节点中
      if(nh.hasChildNodeByNodeName(config.getFilterName())) {
        nh = (INodeHandler)nh.fnn(config.getFilterName());
      }
      //错误页面配置信息
      List<IViewHandler> errorPageList = nh.cnn("error-page");
      for(IViewHandler ele:errorPageList) {
        errorPageMap.put(ele.fnn("error-code").nt(),ele.fnn("location").nt());
      }
    }
    //设置虚拟路径
    this.contextPath = getContextPath(config);
    if(filterUrlPatternPath.length()>0){
      this.contextPath += filterUrlPatternPath;
    }
    //获取配置信息值
    String value = SString.valueOf(config.getInitParameter("extname"));
    if(value.length()>0) {
      this.actionExtName = value;
    }
    try {
      //获取日志过滤器
      XLogFilter xlf = bf.getObject(XLogFilter.class,this);
      //注册到过滤器容器中
      fv.regist(xlf);
    }catch(Exception e) {
      e.printStackTrace();
    }
    try {
      //重新设置类加载器初始化后的相关对象
      fv.setBeanFactory(bf);
      //获取内置Web服务器启动类
      if(bf.beanExists(ServStarter.class)) {
        ServStarter ss = bf.getObject(ServStarter.class,this);
        ss.setContextPath(contextPath); //设置虚拟路径
        try {
          //尝试启动内置服务器（如果没配置端口信息，即端口值小于0，则不会启动内置服务器）
          ss.start(this);
        }catch(Exception e) {
          log.error("Start Navite Web Server Exception:"+e,e);
          e.printStackTrace();
        }
      }
      //从配置文件中获取是否禁用页面缓存功能（不包含图片）
      enabledNoCache    = SBoolean.valueOf(bf.getConfProp().getParameter("enabled_filter_no_cache"));
      //从配置文件中获取是否禁用图片缓存
      enabledImgNoCache = SBoolean.valueOf(bf.getConfProp().getParameter("enabled_filter_img_no_cache"));
      log.startLog("\n--------------------------------------------------------------------\n"
        +"***The Server Start Completed***\nServerInfo:["
        +config.getServletContext().getServerInfo()+"] \nMajorVersion:["
        +config.getServletContext().getMajorVersion()+"] \nMinorVersion:["
        +config.getServletContext().getMinorVersion()+"] \nContextPath:["
        +this.contextPath+"] \nBasePath:["+webBasePath+"]\n"
        +"--------------------------------------------------------------------\n\n");
    }catch(Exception e) {
      e.printStackTrace();
      throw new ServletException(e);
    }
  }

  /**
   * 通过反射方式获取虚拟路径，因为老版本Tomcat没有这个方法
   * 试过好几种方法，最后用这种方法获取虚拟路径
   * @param config 配置信息类
   * @return 虚拟路径
   * 2017年3月30日
   * @author MBG
   */
  private String getContextPath(FilterConfig config) {
    //构建返回值
    String contextPath = null;
    //获取该类中的全部方法
    Method[] methods = config.getServletContext().getClass().getMethods();
    if(methods!=null) {
      int index; //索引
      for(index=0;index<methods.length;index++) {
        if("getContextPath".equals(methods[index].getName())) {
          break;
        }
      }
      try {
        methods[index].setAccessible(true);
        contextPath = SString.valueOf(methods[index].invoke(config.getServletContext()));
        if("/".equals(contextPath)) {
          contextPath = "";
        }
        return contextPath;
      }catch(Exception e) {}
    }
    System.err.println("***The Tomcat Is Too Old Not Find The Method For [config.getServletContext().getContextPath()] Use Key default_context_path from web.xml File");
    //从配置文件中获取虚拟路径
    contextPath = SString.valueOf(config.getInitParameter("default_context_path"));
    if(contextPath.length()<1 || "/".equals(contextPath)) {
      contextPath = "";
    }
    return contextPath;
  }

  /**
   * 初始化服务信息
   * @param config 服务器配置信息
   * @param webBasePath 网站根路径
   * 2016年12月23日
   * @author MBG
   */
  private void initServiceInfo(FilterConfig config,String webBasePath) {
    //服务启动时间
    ServiceInfo.startTime = System.currentTimeMillis();
    //操作系统名称
    ServiceInfo.osName = System.getProperty("os.name");
    //操作系统版本号
    ServiceInfo.osVersion = System.getProperty("os.version");
    //操作系统编译类型
    ServiceInfo.osArch = System.getProperty("os.arch");
    //处理器数量
    ServiceInfo.processorsCount = Runtime.getRuntime().availableProcessors();
    //服务器信息： 例如：Windows 8(内核6.2) amd64  8核
    ServiceInfo.serverInfo = 
      ServiceInfo.osName
      +"(内核"+ServiceInfo.osVersion+") "
      +ServiceInfo.osArch+"("
      +ServiceInfo.processorsCount+"核)";
    //服务器用户
    ServiceInfo.serverUser = BaseUtil.swapString(System.getProperty("user.name"),"\\","/");
    //服务器用户路径
    ServiceInfo.serverUserPath = BaseUtil.swapString(System.getProperty("user.home"),"\\","/");
    //服务器用户信息（综合信息）
    ServiceInfo.serverUserInfo = ServiceInfo.serverUser+"("+ServiceInfo.serverUserPath+")";
    //网站根路径
    ServiceInfo.serverBasePath = webBasePath;
    //java版本
    ServiceInfo.javaVer = System.getProperty("java.version");
    //Java安装目录
    ServiceInfo.javaBasePath = BaseUtil.swapString(System.getProperty("java.home"),"\\","/");
    //系统临时目录
    ServiceInfo.sysTempPath = BaseUtil.swapString(System.getProperty("java.io.tmpdir"),"\\","/");
    //Servlet容器
    ServiceInfo.servletInfo = config.getServletContext().getServerInfo();
    //标记信息登记完毕
    ServiceInfo.hasInfo = true;
  }

  /**
   * 返回虚拟路径
   * @return 虚拟路径
   * 2016年11月25日
   * @author MBG
   */
  public String getContextPath() {
    return contextPath;
  }

  /**
   * 返回网站根路径
   * @return 网站根路径
   * 2017年2月22日
   * @author MBG
   */
  public String getWebBasePath() {
    return webBasePath;
  }

  /**
   * 返回 WEB-INF路径
   * @return  WEB-INF路径
   * 2017年2月22日
   * @author MBG
   */
  public String getWebInfPath() {
    return webInfBasePath;
  }

  /**
   * 通过当前过滤器类实例获取其接口类（抽象父类）
   * @param config 过滤器类实例
   * @return 接口类（抽象父类）
   * 2016年11月7日
   * @author MBG
   */
  protected Class<?> getBaseFilterConfig(Class<?> config) {
    if(config==null) {
      return null;
    }
    if("javax.servlet.FilterConfig".equals(config.getName())) {
      return config;
    }
    Class<?>[] classes = config.getInterfaces();
    if(classes!=null) {
      for(int i=0;i<classes.length;i++) {
        config = getBaseFilterConfig(classes[i]);
        if(config!=null) {
          return config;
        }
      }
    }
    return null;
  }

  /**
   * 覆盖方法
   * 刘虻
   * 2010-5-27 下午02:41:14
   */
  public void unRegist(Object bean) {
    if(bean==null 
        || !(bean instanceof IBean)) {
      return;
    }
    if(bean instanceof IBytesFilter) {
      //模板字节过滤类
      bytesFilterList.remove(bean);
    }
    return;
  }

  /**
   * 覆盖方法
   * 刘虻
   * 2010-5-27 下午02:41:14
   */
  public boolean regist(Object bean) {
    if(bean==null 
        || !(bean instanceof IBean)) {
      return true;
    }
    if(bean instanceof IBytesFilter) {
      //模板字节过滤类
      //构建排序序列
      SortVector<IBytesFilter> sv = new SortVector<IBytesFilter>();
      for(IBytesFilter bf:bytesFilterList) {
        sv.add(bf,bf.getIndex());
      }
      sv.add((IBytesFilter)bean,((IBytesFilter)bean).getIndex());
      bytesFilterList.clear();
      sv.asc();
      while(sv.hasNext()) {
        bytesFilterList.add(sv.nextValue());
      }
    }
    return true;
  }
}
